package org.example;

import java.util.ArrayList;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Synchronize:
 * 无锁-->偏向锁-->轻量级锁-->重量级锁
 * 偏向锁撤销：等到竞争出现才撤销
 * 撤销偏向锁就是将锁对象的对象头恢复成无锁状态或者膨胀成轻量级锁状态，执行撤销动作的前提是锁对象的对象头处于偏向锁状态
 * <p>
 * • 在安全点处（所有线程在安全点处阻塞，没有字节码执行），暂停拥有偏向锁的线程
 * • 查看偏向的线程是否存活，如果已经死亡，那么这个时候会把锁对象头设置成无锁状态。
 * • 如果还存活，如果不在同步块内，那么这个时候会把锁对象头设置成无锁状态。
 * • 如果原获得偏向锁的线程的同步代码块还没执行完，处于临界区之内，这个时候会把原获得偏向锁的线程升级为轻量级锁后继续执行同步代码块
 * • 唤醒暂停的线程
 * <p>
 * Lock:
 * . 比Synchronize灵活
 * . 比Synchronize多了tryLock机制，
 * . lock是可中断锁,而synchronized 不是可中断锁
 *
 * Lock比TryLock
 * - lock() 是没有返回值的；tryLock() 的返回值是 boolean。
 * - 如果被其他线程锁定lock() 一直等锁释放；tryLock() 获取到锁返回true，获取不到锁并直接返回false。
 * - tryLock() 是可以被打断的，被中断的；lock是不可以。
 */
public class SynchronizeAndLockDemo {
    public static void main(String[] args) {
//        LockDemo();
//        tryLockDemo();
        interruptLockDemo();
    }


    public static void LockDemo() {
        ArrayList<Integer> arrayList = new ArrayList<Integer>();
        Lock lock = new ReentrantLock();

        new Thread(() -> {
            Thread thread = Thread.currentThread();

            lock.lock();   // 阻塞式的
            try {
                System.out.println(thread.getName() + "得到了锁");
                for (int i = 0; i < 5; i++) {
                    arrayList.add(i);
                }
            } catch (Exception e) {
                // TODO: handle exception
            } finally {
                System.out.println(thread.getName() + "释放了锁");
                lock.unlock();
            }

        }).start();

        new Thread(() -> {
            Thread thread = Thread.currentThread();

            lock.lock();   // 阻塞式的
            try {
                System.out.println(thread.getName() + "得到了锁");
                for (int i = 0; i < 5; i++) {
                    arrayList.add(i);
                }
            } catch (Exception e) {
                // TODO: handle exception
            } finally {
                System.out.println(thread.getName() + "释放了锁");
                lock.unlock();
            }

        }).start();
    }

    public static void tryLockDemo() {
        ArrayList<Integer> arrayList = new ArrayList<Integer>();
        Lock lock = new ReentrantLock(); // 注意这个地方

        new Thread(() -> {
            Thread thread = Thread.currentThread();
            boolean tryLock = lock.tryLock();  // 非阻塞式的
            System.out.println(thread.getName() + " " + tryLock);
            if (tryLock) {
                try {
                    System.out.println(thread.getName() + "得到了锁");
                    for (int i = 0; i < 5; i++) {
                        arrayList.add(i);
                    }
                } catch (Exception e) {
                    // TODO: handle exception
                } finally {
                    System.out.println(thread.getName() + "释放了锁");
                    lock.unlock();
                }
            } else {
                System.out.println(thread.getName() + "什么也不做");
            }
        }).start();

        new Thread(() -> {
            Thread thread = Thread.currentThread();
            boolean tryLock = lock.tryLock();  // 非阻塞式的
            System.out.println(thread.getName() + " " + tryLock);
            if (tryLock) {
                try {
                    System.out.println(thread.getName() + "得到了锁");
                    for (int i = 0; i < 5; i++) {
                        arrayList.add(i);
                    }
                } catch (Exception e) {
                    // TODO: handle exception
                } finally {
                    System.out.println(thread.getName() + "释放了锁");
                    lock.unlock();
                }
            } else {
                System.out.println(thread.getName() + "什么也不做");
            }
        }).start();
    }


    /**
     * lockInterruptibly给用户了终止的等待线程的权利
     *  观察现象：如果thread-0得到了锁，阻塞。。。
     *  thread-1尝试获取锁，如果拿不到，会等待，但是可以被中断等待。
     */
    public static void interruptLockDemo() {
        Lock lock = new ReentrantLock();

        Thread thread0 = new Thread(() -> {
            try {
                lock.lockInterruptibly();   //注意，如果需要正确中断等待锁的线程，必须将获取锁放在外面，然后将InterruptedException抛出
                System.out.println("thread0 得到了锁");
                long startTime = System.currentTimeMillis();
                for (; ; ) {
                    if (System.currentTimeMillis() - startTime >= 10000) {
                        break;
                    } else {
                        System.out.println("thread0在工作");
                        Thread.sleep(1000);
                    }
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } finally {
                System.out.println("thread0 执行finally");
                lock.unlock();
                System.out.println("thread0 释放了锁");
            }
        });

        Thread thread1 = new Thread(() -> {
            try {
                lock.lockInterruptibly();   //注意，如果需要正确中断等待锁的线程，必须将获取锁放在外面，然后将InterruptedException抛出
                System.out.println("thread1得到了锁");
                long startTime = System.currentTimeMillis();
                for (; ; ) {
                    if (System.currentTimeMillis() - startTime >= 10000) {
                        break;
                    } else {
                        System.out.println("thread1在工作");
                        Thread.sleep(1000);
                    }
                    //插入数据
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } finally {
                System.out.println("thread1执行finally");
                lock.unlock();
                System.out.println("thread1释放了锁");
            }
        });

        thread0.start();
        try {
            Thread.sleep(1000);  // (1)等了1秒钟之后，如果还thread1启动
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread1.start();

        try {
            Thread.sleep(2000);  // (1)等了两秒钟之后，如果还thread1还拿不到锁
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // (2)就中断他呗，注意：lockInterruptibly()只有这个方法才是可中断的
        thread1.interrupt();   //调用中断方法来测试能否中断等待中的线程
        System.out.println("=====================");
    }
}
